package com.obelisco.modelo.dao.hibernate;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.*;
import org.hibernate.Session;
import org.hibernate.criterion.*;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import com.obelisco.modelo.dao.DAOException;


public class HibernateDAO extends HibernateDaoSupport implements IHibernateDAO {

   private boolean cacheQueries = false;

   private String queryCacheRegion;

   private Log log = LogFactory.getLog(HibernateDAO.class);
   
   /**
    * Empty constructor
    */
   public HibernateDAO() {
     super();
   }
      

   public void flushSession() throws DAOException {
      if (getSession() != null)
         try {
            getSession().flush();
         } catch (HibernateException e) {
            log.error("Fail to flush session", e);
            throw new DAOException("Fail to flush session", e);
         }
   }


   public void clearSession() throws DAOException {
         try {
        	 getHibernateTemplate().clear();
         } catch (HibernateException ex) {
            log.error("Fail to clear session", ex);
            throw new DAOException("Fail to clear session", ex);
         }
   }

   public void update(Object persistentObject) throws DAOException {
      try {
    	  getHibernateTemplate().update(persistentObject);
      } catch (HibernateException ex) {
         log.error("Fail to update", ex);
         throw new DAOException("Fail to update", ex);
      } 
   }
   
   public void delete(Object persistentObject) throws DAOException {
      try {
    	  getHibernateTemplate().delete(persistentObject);
      } catch (HibernateException ex) {
         log.error("Fail to delete", ex);
         throw new DAOException("Fail tlo delete", ex);
      } 
   }
   
   
   public List queryByExample(Object persistentObject) throws DAOException{
	      List objs = new ArrayList();
	      try {
	    	  objs = getHibernateTemplate().findByExample(persistentObject);
	      } catch (HibernateException ex) {
	         log.error("Fail to find all  objects", ex);
	         throw new DAOException("Fail to find all  objects", ex);
	      } 
	      return objs;  
	   }

   
   public List findAll(Class clazz) throws DAOException {
      List objs = new ArrayList();
      try {
    	  objs = getHibernateTemplate().loadAll(clazz);
      } catch (HibernateException ex) {
         log.error("Fail to find all  objects", ex);
         throw new DAOException("Fail to find all  objects", ex);
      } 
      return objs;
   }
   
   public List findByProperty(Class clazz, Criterion restriction) throws DAOException {
      List objs = new ArrayList();
      try {
         objs = getSession().createCriteria(clazz).add(restriction).list();
      } catch (HibernateException ex) {
         log.error("Fail to find objects by property", ex);
         throw new DAOException("Fail to find objects by property", ex);
      }
      return objs;
   }
   
   public List findByCriterions(Class clazz, List restrictions)
      throws DAOException {
      List objs = new ArrayList();
      
      try {
         Session session = getSession();
         Criteria criteria = session.createCriteria(clazz);
         Iterator it = restrictions.iterator();
         while (it.hasNext())
            criteria.add((Criterion) it.next());
         objs = criteria.list();
      } catch (HibernateException ex) {
         log.error("Fail to find objects by criterions", ex);
         throw new DAOException("Fail to find objects by criterions", ex);
      } 
      return objs;
   }
   

   public List findByCriterions(Class clazz, List restrictions, int firstResult, int maxResult) throws DAOException {
      List objs = new ArrayList();
      
      try {
         Session session = getSession();
         Criteria criteria = session.createCriteria(clazz).setFirstResult(firstResult).setMaxResults(maxResult);
         Iterator it = restrictions.iterator();
         while (it.hasNext())
            criteria.add((Criterion) it.next());
         objs = criteria.list();
      } catch (HibernateException ex) {
         log.error("Fail to find objects by criterions", ex);
         throw new DAOException("Fail to find objects by criterions", ex);
      } 
      return objs;
   }
   

   public List findBySQLQuery(String sqlQuery, String aliasName, Class clazz) throws DAOException {
      List result = new ArrayList();
      try {
         Session session = getSession();
         result = session.createSQLQuery(sqlQuery).addEntity(aliasName, clazz).list();
      } catch (HibernateException ex) {
         log.error("Fail to execute query", ex);
         throw new DAOException("Fail to execute query", ex);
      } 
      return result;
   }


   public List findByHQLQuery(String hqlQuery) throws DAOException {
      List result = new ArrayList();
      try {
         Session session = getSession();
         result = session.createQuery(hqlQuery).list();
      } catch (HibernateException ex) {
         log.error("Fail to execute query", ex);
         throw new DAOException("Fail to execute query", ex);
      } 
      return result;
   }
   

   public List findByNamedParam(String queryString, String paramName, Object value) throws DAOException {
      return findByNamedParam(queryString, new String[] { paramName }, new Object[] { value });
   }
   

   public List findByNamedParam(String queryString, String paramName, Object value, int firstResult, int maxResult) throws DAOException {
      return findByNamedParam(queryString, new String[] { paramName }, new Object[] { value }, firstResult, maxResult);
   }
   

   public List findByNamedParam(final String queryString, final String[] paramNames, final Object[] values) throws DAOException {
      try {
         if (paramNames.length != values.length) {
            throw new IllegalArgumentException("Length of paramNames array must match length of values array");
         }
         Session session = getSession();
         Query queryObject = session.createQuery(queryString);
         
         prepareQuery(queryObject);
         if (values != null) {
            for (int i = 0; i < values.length; i++) {
               applyNamedParameterToQuery(queryObject, paramNames[i], values[i]);
            }
         }
         
         return queryObject.list();

      } catch (HibernateException ex) {
         log.error("Fail to find", ex);
         throw new DAOException("Fail to find", ex);
      } 
   }
   

   public List findByNamedParam(final String queryString, final String[] paramNames, final Object[] values,
      final int firstResult, final int maxResult) throws DAOException {
      if (paramNames.length != values.length) {
         throw new IllegalArgumentException("Length of paramNames array must match length of values array");
      }
      try {
         Session session = getSession();
         Query queryObject = session.createQuery(queryString);
         queryObject.setFirstResult(firstResult);
         queryObject.setMaxResults(maxResult);
         prepareQuery(queryObject);
         if (values != null) {
            for (int i = 0; i < values.length; i++) {
               applyNamedParameterToQuery(queryObject, paramNames[i], values[i]);
            }
         }
         return queryObject.list();
      } catch (HibernateException ex) {
         log.error("Fail to find by named param");
         throw new DAOException("Fail to find by named param", ex);
      }
   }
   

   public List findByValueBean(final String queryString, final Object valueBean) throws DAOException {
      Session session = getSession();
      try {
         Query queryObject = session.createQuery(queryString);
         prepareQuery(queryObject);
         queryObject.setProperties(valueBean);
         return queryObject.list();
      } catch (HibernateException ex) {
         log.error("Fail to find by valueBean", ex);
         throw new DAOException("Fail to find by valueBeam", ex);
      }
   }
   

   public List findByNamedQuery(String queryName) throws DAOException {
      return findByNamedQuery(queryName, (Object[]) null);
   }
   

   public List findByNamedQuery(String queryName, Object value) throws DAOException {
      return findByNamedQuery(queryName, new Object[] { value });
   }
   

   public List findByNamedQuery(final String queryName, final Object[] values) throws DAOException {
      Session session = getSession();
      try {
         Query queryObject = session.getNamedQuery(queryName);
         prepareQuery(queryObject);
         if (values != null) {
            for (int i = 0; i < values.length; i++) {
               queryObject.setParameter(i, values[i]);
            }
         }
         return queryObject.list();
      } catch (HibernateException ex) {
         log.error("Fail to find by Named query", ex);
         throw new DAOException("Fail to find by Named query", ex);
      } 
   }
   

   public List findByNamedQueryAndNamedParam(String queryName, String paramName, Object value) throws DAOException {
      return findByNamedQueryAndNamedParam(queryName, new String[] { paramName }, new Object[] { value });
   }

   public List findByNamedQueryAndNamedParam(final String queryName, final String[] paramNames, final Object[] values) throws DAOException {
      if (paramNames != null && values != null && paramNames.length != values.length) {
         throw new IllegalArgumentException("Length of paramNames array must match length of values array");
      }
      Session session = getSession();
      try {
         Query queryObject = session.getNamedQuery(queryName);
         prepareQuery(queryObject);
         if (values != null) {
            for (int i = 0; i < values.length; i++) {
            applyNamedParameterToQuery(queryObject, paramNames[i], values[i]);
            }
         }
      return queryObject.list();
      } catch (HibernateException ex) {
         log.error("Fail to find by Named query and Named Param", ex);
         throw new DAOException("Fail to find by Named query and Named Param", ex);
      } 
   }
   

   public List findByNamedQueryAndValueBean(final String queryName, final Object valueBean) throws DAOException {
      Session session = getSession();
      try {
         Query queryObject = session.getNamedQuery(queryName);
         prepareQuery(queryObject);
         queryObject.setProperties(valueBean);
         return queryObject.list();
      } catch (HibernateException ex) {
         log.error("Fail to find by Named query and value bean", ex);
         throw new DAOException("Fail to find by Named query and value bean", ex);
      } 
   }
   
   protected void prepareQuery(Query queryObject) {
      if (isCacheQueries()) {
         queryObject.setCacheable(true);
         if (getQueryCacheRegion() != null) {
            queryObject.setCacheRegion(getQueryCacheRegion());
         }
      }
   }
   

   protected void applyNamedParameterToQuery(Query queryObject, String paramName, Object value) throws HibernateException {
      if (value instanceof Collection) {
         queryObject.setParameterList(paramName, (Collection) value);
      } else if (value instanceof Object[]) {
         queryObject.setParameterList(paramName, (Object[]) value);
      } else {
         queryObject.setParameter(paramName, value);
      }
   }
   

   public boolean isCacheQueries() {
      return cacheQueries;
   }
   
   public String getQueryCacheRegion() {
      return queryCacheRegion;
   }   
   

   public Serializable save(Object persistentObject) throws DAOException {
      try {
         Serializable id = getHibernateTemplate().save(persistentObject);
         return id;
      } catch (HibernateException ex) {
         log.error("Fail to save persistentObject", ex);
         throw new DAOException("Fail to save persistentObject", ex);
      } 
   }
   

   public void saveOrUpdate(Object persistentObject) throws DAOException {
      try {
    	  getHibernateTemplate().saveOrUpdate(persistentObject);
      } catch (HibernateException ex) {
         log.error("Fail to save or update persistentObject", ex);
         throw new DAOException("Fail to save or update persistentObject", ex);
      } 
   }

   
   public List find(String queryString, int firstResult, int maxResult) throws DAOException {
      return find(queryString, (Object[]) null, firstResult, maxResult);
   }
   

   public List find(String queryString) throws DAOException {
      return find(queryString, (Object[]) null);
   }
   

   public List find(String queryString, Object value) throws DAOException {
      return find(queryString, new Object[] { value });
   }
   
   public List find(final String queryString, final Object[] values) throws DAOException {
      Session session = this.getSession();
      try {
         Query queryObject = session.createQuery(queryString);
         prepareQuery(queryObject);
         if (values != null) {
            for (int i = 0; i < values.length; i++) {
               queryObject.setParameter(i, values[i]);
            }
         }
         return queryObject.list();
      } catch (HibernateException ex) {
         log.error("Fail to find by query string", ex);
         throw new DAOException("Fail to find by query string", ex);
      } 
   }
   

   public List find(final String queryString, final Object[] values, final int firstResult, final int maxResult) throws DAOException {
      Session session = this.getSession();
      try {
         Query queryObject = session.createQuery(queryString).setFirstResult(firstResult).setMaxResults(maxResult);
         prepareQuery(queryObject);
         if (values != null) {
            for (int i = 0; i < values.length; i++) {
               queryObject.setParameter(i, values[i]);
            }
         }
         return queryObject.list();
      } catch (HibernateException ex) {
         log.error("Fail to find by query string", ex);
         throw new DAOException("Fail to find by query string", ex);
      } 
   }
   
   public List loadAll(final Class entityClass) throws DAOException {
      try {        
    	 return getHibernateTemplate().loadAll(entityClass);
      } catch (HibernateException ex) {
         log.error("Fail to load all ", ex);
         throw new DAOException("Fail to load all", ex);
      } 
   }
   
   public List loadAll(final Class entityClass, final int firstResult, final int maxResult) throws DAOException {
      Session session = this.getSession();      
      try {
         Criteria criteria = session.createCriteria(entityClass).setFirstResult(firstResult).setMaxResults(maxResult);
         return criteria.list();
      } catch (HibernateException ex) {
         log.error("Fail to load all ", ex);
         throw new DAOException("Fail to load all", ex);
      } 
   }
   
   private Session openSession(){
	   return  getSession();   
   }
}